package com.wande.dataplatform.filecollection.service.impl;

import cn.hutool.core.util.StrUtil;
import com.wande.dataplatform.filecollection.common.enums.FileCollectionErrorCode;
import com.wande.dataplatform.filecollection.common.exception.FileCollectionException;
import com.wande.dataplatform.filecollection.domain.dto.FieldInfo;
import com.wande.dataplatform.filecollection.domain.dto.ParseResult;
import com.wande.dataplatform.filecollection.parser.IFileParser;
import com.wande.dataplatform.filecollection.parser.ParserConfig;
import com.wande.dataplatform.filecollection.service.IFileParsingService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import jakarta.annotation.PostConstruct;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 文件解析服务实现类
 * 管理所有文件解析器，提供统一的解析入口
 *
 * @author dataplatform
 * @date 2025-01-24
 */
@Slf4j
@Service
public class FileParsingServiceImpl implements IFileParsingService {

    /**
     * 解析器注册表
     * Key: 文件类型（大写）
     * Value: 解析器实例
     */
    private final Map<String, IFileParser> parserRegistry = new ConcurrentHashMap<>();

    /**
     * 解析器缓存
     * 用于缓存最近使用的解析器
     */
    private final Map<String, IFileParser> parserCache = new ConcurrentHashMap<>();

    @Autowired(required = false)
    private List<IFileParser> parsers;

    /**
     * 初始化解析器注册表
     * 自动注册所有实现了IFileParser接口的Spring Bean
     */
    @PostConstruct
    public void init() {
        if (parsers != null && !parsers.isEmpty()) {
            for (IFileParser parser : parsers) {
                String fileType = parser.getSupportedFileType();
                if (StrUtil.isNotBlank(fileType)) {
                    // 支持多个文件类型（用逗号分隔）
                    String[] types = fileType.split(",");
                    for (String type : types) {
                        registerParser(type.trim(), parser);
                    }
                }
            }
            log.info("Registered {} file parsers: {}", parserRegistry.size(), parserRegistry.keySet());
        } else {
            log.warn("No file parsers found in Spring context");
        }
    }

    @Override
    public void registerParser(String fileType, IFileParser parser) {
        if (StrUtil.isBlank(fileType)) {
            throw new IllegalArgumentException("File type cannot be blank");
        }
        if (parser == null) {
            throw new IllegalArgumentException("Parser cannot be null");
        }

        String normalizedType = fileType.toUpperCase();
        parserRegistry.put(normalizedType, parser);
        log.debug("Registered parser for file type: {}", normalizedType);
    }

    @Override
    public IFileParser getParser(String fileType) {
        if (StrUtil.isBlank(fileType)) {
            throw new FileCollectionException(FileCollectionErrorCode.INVALID_PARAMETER, "File type cannot be blank");
        }

        String normalizedType = fileType.toUpperCase();

        // 先从缓存中获取
        IFileParser cachedParser = parserCache.get(normalizedType);
        if (cachedParser != null) {
            return cachedParser;
        }

        // 从注册表中获取
        IFileParser parser = parserRegistry.get(normalizedType);
        if (parser == null) {
            throw new FileCollectionException(FileCollectionErrorCode.UNSUPPORTED_FILE_TYPE, "No parser found for file type: " + fileType);
        }

        // 放入缓存
        parserCache.put(normalizedType, parser);
        return parser;
    }

    @Override
    public List<FieldInfo> detectFields(InputStream inputStream, String fileType, ParserConfig config) {
        if (inputStream == null) {
            throw new FileCollectionException(FileCollectionErrorCode.INVALID_PARAMETER, "Input stream cannot be null");
        }

        IFileParser parser = getParser(fileType);
        ParserConfig effectiveConfig = config != null ? config : ParserConfig.builder().build();

        try {
            long startTime = System.currentTimeMillis();
            List<FieldInfo> fields = parser.detectFields(inputStream, effectiveConfig);
            long elapsedTime = System.currentTimeMillis() - startTime;

            log.info("Detected {} fields from {} file in {}ms", fields.size(), fileType, elapsedTime);
            return fields;

        } catch (Exception e) {
            log.error("Failed to detect fields from {} file", fileType, e);
            throw new FileCollectionException(FileCollectionErrorCode.PARSE_ERROR, "Failed to detect fields: " + e.getMessage(), e);
        }
    }

    @Override
    public ParseResult parseFile(InputStream inputStream, String fileType, ParserConfig config) {
        if (inputStream == null) {
            throw new FileCollectionException(FileCollectionErrorCode.INVALID_PARAMETER, "Input stream cannot be null");
        }

        IFileParser parser = getParser(fileType);
        ParserConfig effectiveConfig = config != null ? config : ParserConfig.builder().build();

        ParseResult.ParseResultBuilder resultBuilder = ParseResult.builder();
        long startTime = System.currentTimeMillis();

        try {
            // 检测字段
            List<FieldInfo> fields = parser.detectFields(inputStream, effectiveConfig);
            resultBuilder.fields(fields);

            // 解析数据
            List<Map<String, Object>> data = new ArrayList<>();
            Iterator<Map<String, Object>> iterator = parser.parse(inputStream, effectiveConfig);

            long totalRecords = 0;
            long successRecords = 0;
            long failedRecords = 0;

            while (iterator.hasNext()) {
                try {
                    Map<String, Object> record = iterator.next();
                    data.add(record);
                    totalRecords++;
                    successRecords++;
                } catch (Exception e) {
                    failedRecords++;
                    log.warn("Failed to parse record {}: {}", totalRecords, e.getMessage());

                    ParseResult.ParseError error = ParseResult.ParseError.builder()
                            .recordIndex((int) totalRecords)
                            .errorType("PARSE_ERROR")
                            .errorMessage(e.getMessage())
                            .build();
                    resultBuilder.errors(Collections.singletonList(error));
                }
            }

            long elapsedTime = System.currentTimeMillis() - startTime;

            resultBuilder
                    .success(failedRecords == 0)
                    .data(data)
                    .totalRecords(totalRecords)
                    .successRecords(successRecords)
                    .failedRecords(failedRecords)
                    .elapsedTime(elapsedTime);

            log.info("Parsed {} file: {} total, {} success, {} failed in {}ms",
                    fileType, totalRecords, successRecords, failedRecords, elapsedTime);

            return resultBuilder.build();

        } catch (Exception e) {
            log.error("Failed to parse {} file", fileType, e);

            long elapsedTime = System.currentTimeMillis() - startTime;
            return resultBuilder
                    .success(false)
                    .errorMessage(e.getMessage())
                    .elapsedTime(elapsedTime)
                    .build();
        }
    }

    @Override
    public void parseFileStream(InputStream inputStream, String fileType, ParserConfig config,
                               Consumer<Map<String, Object>> consumer) {
        if (inputStream == null) {
            throw new FileCollectionException(FileCollectionErrorCode.INVALID_PARAMETER, "Input stream cannot be null");
        }
        if (consumer == null) {
            throw new FileCollectionException(FileCollectionErrorCode.INVALID_PARAMETER, "Consumer cannot be null");
        }

        IFileParser parser = getParser(fileType);
        ParserConfig effectiveConfig = config != null ? config : ParserConfig.builder().build();

        try {
            long startTime = System.currentTimeMillis();
            parser.parseStream(inputStream, effectiveConfig, consumer);
            long elapsedTime = System.currentTimeMillis() - startTime;

            log.info("Stream parsed {} file in {}ms", fileType, elapsedTime);

        } catch (Exception e) {
            log.error("Failed to stream parse {} file", fileType, e);
            throw new FileCollectionException(FileCollectionErrorCode.PARSE_ERROR, "Failed to stream parse file: " + e.getMessage(), e);
        }
    }

    @Override
    public boolean validateFile(InputStream inputStream, String fileType, ParserConfig config) {
        if (inputStream == null) {
            return false;
        }

        try {
            IFileParser parser = getParser(fileType);
            ParserConfig effectiveConfig = config != null ? config : ParserConfig.builder().build();

            boolean valid = parser.validate(inputStream, effectiveConfig);
            log.debug("Validation result for {} file: {}", fileType, valid);

            return valid;

        } catch (Exception e) {
            log.warn("File validation failed for type {}: {}", fileType, e.getMessage());
            return false;
        }
    }

    @Override
    @Cacheable(value = "fileTypes", unless = "#result == null || #result.isEmpty()")
    public List<String> getSupportedFileTypes() {
        return new ArrayList<>(parserRegistry.keySet());
    }

    /**
     * 清除解析器缓存
     */
    public void clearCache() {
        parserCache.clear();
        log.info("Parser cache cleared");
    }

    /**
     * 获取解析器统计信息
     *
     * @return 统计信息
     */
    public Map<String, Object> getStatistics() {
        Map<String, Object> stats = new HashMap<>();
        stats.put("registeredParsers", parserRegistry.size());
        stats.put("cachedParsers", parserCache.size());
        stats.put("supportedFileTypes", getSupportedFileTypes());

        Map<String, String> parserMapping = parserRegistry.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        e -> e.getValue().getClass().getSimpleName()
                ));
        stats.put("parserMapping", parserMapping);

        return stats;
    }
}
